#include "itemtreemodel.h"


#include <QDebug>
#include <QMetaProperty>
#include <QStringList>


ItemTreeModel::ItemTreeModel(QObject *parent) :
    QAbstractItemModel(parent)
{
    qRegisterMetaType<ModelItem*>();

    m_changed = false;
}

ItemTreeModel::ItemTreeModel(ModelItem *root, QObject *parent) :
    QAbstractItemModel(parent)
{
    qRegisterMetaType<ModelItem*>();

    setRoot(root);
    m_changed = false;
}

ItemTreeModel::~ItemTreeModel()
{
    if (m_root.isNull())
    {
        return;
    }

    if (m_root->parent())
    {
        return;
    }

    m_root->deleteLater();
}

QModelIndex ItemTreeModel::index(int row, int column, const QModelIndex &parent) const
{
    if (row < 0 || !hasIndex(row, column, parent))
    {
        return QModelIndex();
    }

    ModelItem* parentItem = 0;
    if (!parent.isValid())
    {
        parentItem = root();
    }
    else
    {
        parentItem = itemFromIndex(parent);
    }

    if (!parentItem)
    {
        return QModelIndex();
    }

//    QList<ModelItem*> items = parentItem->findChildren<ModelItem*>(QString(), Qt::FindDirectChildrenOnly);
    QList<ModelItem*> items = parentItem->childItems();

    ModelItem* item = items.value(row, 0);
    if (!item)
    {
        return QModelIndex();
    }

    return createIndex(row, column, item);
}

QModelIndex ItemTreeModel::parent(const QModelIndex &child) const
{
    if (!child.isValid())
    {
        return QModelIndex();
    }

    ModelItem* childItem = itemFromIndex(child);
    if (childItem == root())
    {
        return QModelIndex();
    }

    ModelItem* parentItem = childItem->parentItem();
    if (!parentItem || parentItem == root())
    {
        return QModelIndex();
    }

    ModelItem* ancestorItem = parentItem->parentItem();
    if (!ancestorItem)
    {
        return QModelIndex();
    }

//    QList<ModelItem*> items = ancestorItem->findChildren<ModelItem*>(QString(), Qt::FindDirectChildrenOnly);
    QList<ModelItem*> items = ancestorItem->childItems();
    if (!items.contains(parentItem))
    {
        return QModelIndex();
    }

    int row = items.indexOf(parentItem);
    return createIndex(row, 0, parentItem);
}

int ItemTreeModel::rowCount(const QModelIndex &parent) const
{
    ModelItem* parentItem = 0;
    if (!parent.isValid())
    {
        parentItem = root();
    }
    else
    {
        parentItem = itemFromIndex(parent);
    }

    if (!parentItem)
    {
        return 0;
    }

    return parentItem->findChildren<ModelItem*>(QString(), Qt::FindDirectChildrenOnly).count();
}

int ItemTreeModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent)

    if (!root())
    {
        return 0;
    }

    int cols = 1;
    foreach (const ModelItem* item, root()->findChildren<const ModelItem*>())
    {
        const QMetaObject* mo = item->metaObject();
        int propertyCount = mo->propertyCount()-1;

//        QStringList tmp;
//        for (int i = 1; i <= propertyCount; i++)
//        {
//            QMetaProperty mp = mo->property(i);
//            tmp << mp.name();
//        }

//        qDebug() << item->name() << "properties:" << tmp.join(", ") << propertyCount;


        if (propertyCount <= cols)
        {
            continue;
        }

        cols = propertyCount;
    }

    return cols;
}

QVariant ItemTreeModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
    {
        return QVariant();
    }

    ModelItem* item = itemFromIndex(index);
    if (!item)
    {
        return QVariant();
    }


    const QMetaObject* mo = item->metaObject();
    int col = index.column();

    switch (role)
    {
    case Qt::EditRole:
    {
        QMetaProperty mp = mo->property(col+1);
        if (!mp.isReadable())
        {
            return QString();
        }

        return mp.read(item);
    }

    case Qt::DisplayRole:
    {
        QMetaProperty mp = mo->property(col+1);
        if (!mp.isReadable())
        {
//            qDebug() << Q_FUNC_INFO << "Data at col" << col << "is not readable";
            return QString();
        }

        QVariant ret = mp.read(item);
        if (mp.isEnumType())
        {
            QMetaEnum me = mp.enumerator();
            return me.valueToKey(ret.toInt());
        }

//        qDebug() << Q_FUNC_INFO << "Data at col" << col << "is" << ret.toString();
        return ret;
    }


    case Role_PATH:
        return item->objectName();

    case Role_SORT:
        return item->sortData();

    case Role_TYPE:
    {
        int type = item->type();
        if (type <= QMetaType::User)
        {
            qDebug() << Q_FUNC_INFO << "A";
            return QVariant();
        }
        return type;
    }

    default:
        break;
    }


    return QVariant();
}

bool ItemTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (!index.isValid() || role != Qt::EditRole)
    {
        return false;
    }

    ModelItem* item = itemFromIndex(index);
    if (!item)
    {
        return false;
    }

    const QMetaObject* mo = item->metaObject();
    int col = index.column();

    QMetaProperty mp = mo->property(col+1);
    if (!mp.isWritable())
    {
        return false;
    }

    if (!mp.write(item, value))
    {
        return false;
    }

    emit dataChanged(index, index);
    return true;
}

Qt::ItemFlags ItemTreeModel::flags(const QModelIndex &index) const
{
    ModelItem* item = itemFromIndex(index);
    if (!item)
    {
        return false;
    }

    const QMetaObject* mo = item->metaObject();

    QMetaProperty mp = mo->property(index.column()+1);
    if (!mp.isWritable())
    {
        return QAbstractItemModel::flags(index);
    }

    return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
}


void ItemTreeModel::setRoot(ModelItem *root)
{
    if (root == m_root.data())
    {
        return;
    }

    if (!m_root.isNull())
    {
        disconnect(m_root.data(), SIGNAL(sigChanged()), this, SLOT(onChanged()));
    }

    beginResetModel();

    m_root = root;
    if (!m_root.isNull())
    {
        connect(m_root.data(), SIGNAL(sigChanged()), this, SLOT(onChanged()));
    }
    m_changed = false;

    endResetModel();
}

ModelItem *ItemTreeModel::root() const
{
    return m_root;
}

bool ItemTreeModel::isChanged() const
{
    return m_changed;
}

ModelItem *ItemTreeModel::itemFromIndex(const QModelIndex &index)
{
    return reinterpret_cast<ModelItem*>(index.internalPointer());
}

void ItemTreeModel::onChanged(bool changed)
{
    m_changed = changed;
}

